package net.stuffrepos.bricknetcenter.util.gui.inputcomponent;

import java.awt.Color;
import net.stuffrepos.bricknetcenter.util.validation.Constraint;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import javax.swing.JLabel;
import net.stuffrepos.bricknetcenter.util.gui.GapedPanel;
import net.stuffrepos.bricknetcenter.util.gui.inputcomponent.InputComponentValueParser.ValueResult;
import net.stuffrepos.util.cache.CacheableValue;
import net.stuffrepos.util.listeners.Listener;
import net.stuffrepos.util.listeners.ListenerManager;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 *
 * @author Eduardo H. Bogoni <eduardobogoni@gmail.com>
 */
public class InputComponent<InputComponentType extends Component, ValueType> extends GapedPanel {

    private static final Log log = LogFactory.getLog(InputComponent.class);
    private static final Color INVALID_BACKGROUND_COLOR = new Color(0xFF, 0x7F, 0x7f);
    private static final Color INVALID_FOREGROUND_COLOR = Color.WHITE;
    private ListenerManager<InputComponent> listenerManager = new ListenerManager<InputComponent>(this);
    private CacheableValue<ValueResult<ValueType>> value = new CacheableValue<ValueResult<ValueType>>() {

        @Override
        protected ValueResult<ValueType> calculate() {
            return valueParser.fromComponent(inputComponent);
        }
    };

    public boolean isValidValue() {
        if (value.getValue().isValid()) {
            return getConstraint().isValid(value.getValue().getValue());
        } else {
            return false;
        }
    }

    public InputComponent setEditable(boolean editable) {
        valueParser.setEditable(inputComponent, editable);
        return this;
    }

    public Constraint getConstraint() {
        return constraint;
    }

    public static enum LabelPosition {

        Left,
        Top
    }
    private final InputComponentType inputComponent;
    private final Constraint constraint;
    private final JLabel label;
    private final Color backgroundColor;
    private final Color foregroundColor;
    private final Color inputBackgroundColor;
    InputComponentValueParser<InputComponentType, ValueType> valueParser;

    public InputComponent(InputComponentType inputComponent,
            InputComponentValueParser<InputComponentType, ValueType> valueParser,
            Constraint constraint) {
        this.setConstraintProperty(ConstraintProperty.WidthX, 1.0);
        this.label = new JLabel();
        this.inputComponent = inputComponent;
        this.setLabelPosition(LabelPosition.Top);
        this.foregroundColor = label.getForeground();
        this.backgroundColor = this.getBackground();
        this.inputBackgroundColor = inputComponent.getBackground();
        this.constraint = constraint;
        this.valueParser = valueParser;

        this.prepare(inputComponent);

        fireValueChanged();
        this.validate();
    }

    public InputComponent<InputComponentType, ValueType> setLabelText(String text) {
        this.label.setText(text);
        return this;
    }

    public InputComponent<InputComponentType, ValueType> setValue(ValueType value) {
        valueParser.toComponent(inputComponent, value);
        return this;
    }

    public ValueType getValue() {
        return value.getValue().getValue();
    }

    public InputComponent<InputComponentType, ValueType> setLabelPosition(LabelPosition labelPosition) {
        this.removeAll();
        if (LabelPosition.Left.equals(labelPosition)) {
            this.getRow(0).
                    setConstraintProperty(ConstraintProperty.Fill, GridBagConstraints.VERTICAL).
                    getSub(0).add(label).
                    getSub(1).add(inputComponent, 1, 1, GridBagConstraints.BOTH);
        } else {
            this.getColumn(0).
                    setConstraintProperty(ConstraintProperty.Fill, GridBagConstraints.HORIZONTAL).
                    getSub(0).add(label).
                    getSub(1).add(inputComponent, 1, 1, GridBagConstraints.BOTH);
        }


        return this;
    }

    protected void prepare(InputComponentType component) {
        //Do Nothing
    }

    public InputComponentType getInputComponent() {
        return inputComponent;
    }

    public void fireValueChanged() {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                value.clear();
                if (value.isChanged()) {
                    if (value.getValue().isValid()) {
                        label.setForeground(foregroundColor);
                        InputComponent.this.setBackground(backgroundColor);
                        inputComponent.setBackground(inputBackgroundColor);
                    } else {
                        label.setForeground(INVALID_FOREGROUND_COLOR);
                        InputComponent.this.setBackground(INVALID_BACKGROUND_COLOR);
                        inputComponent.setBackground(INVALID_BACKGROUND_COLOR);
                    }


                    listenerManager.fireChange();
                }
            }
        });

    }

    public void addValueListener(Listener<InputComponent> listener) {
        listenerManager.addListener(listener);
    }

    @Override
    public void setEnabled(boolean enabled) {
        inputComponent.setEnabled(enabled);
        label.setEnabled(enabled);
        super.setEnabled(enabled);
    }
}
